﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using CMPPProtocol;
using System.Security.Cryptography;
using System.Threading;
using System.Threading.Tasks;
using CMPPCore.Message;
using CMPPProtocol.Message;
using System.Collections;

namespace CMPPCore
{
    /// <summary>
    /// CMPP2.0,CMPP3.0核心
    /// </summary>
    public sealed class SMSClient
    {

        #region SP  登录信息
        /// <summary>
        /// SP 企业代码。
        /// </summary>
        private string m_strSPID;
        /// <summary>
        /// SP 密码。
        /// </summary>
        private string m_strPassword;
        /// <summary>
        /// 短信网关地址。
        /// </summary>
        private string m_strAddress;
        /// <summary>
        /// 短信网关端口号。
        /// </summary>
        private int m_iPort;

        #endregion

        #region SP 版本信息

        /// <summary>
        /// CMPP协议
        /// </summary>
        private ProtocolStrategy m_CMPPProtocol;


        #endregion

        #region 消息队列信息

        /// <summary>
        /// 消息队列
        /// </summary>
        private Queue<DATA_PACKAGE> m_MESSAGEQueue = new Queue<DATA_PACKAGE>();

        /// <summary>
        /// 滑动窗口
        /// </summary>
        private MessageSlideWindow[] m_SlideWindow = new MessageSlideWindow[16];

        #endregion

        #region 网络通讯相关
        private TcpClient m_TcpClient;

        private NetworkStream m_NetworkStream;
        /// <summary>
        /// TcpClient 接收和发送超时（以秒为单位）。
        /// </summary>
        private int m_iTcpClientTimeout = 5;
        /// <summary>
        /// TcpClient 发送间隔，以毫秒为单位。
        /// </summary>
        private int m_iSendSpan = 14;
        /// <summary>
        /// TcpClient 接收间隔，以毫秒为单位。
        /// </summary>
        private int m_iReceiveSpan = 5;
        /// <summary>
        /// ACTIVETEST 的时间间隔（C，以秒为单位）。
        /// </summary>
        /// <remarks>
        /// 当信道上没有数据传输时，通信双方应每隔时间 C 发送链路检测包以维持此连接。
        /// </remarks>
        private int m_iActiveTestSpan = 30;

        /// <summary>
        /// 最后一次发送时间
        /// </summary>
        private DateTime? m_iLastSendTime;
        /// <summary>
        /// 响应超时时间（T,以秒为单位）。
        /// </summary>
        /// <remarks>
        /// 网关与 SP 之间、网关之间的消息发送后等待 T 秒后未收到响应，应立即重发，再连续发送 N-1 次后仍未得到响应则停发。
        /// </remarks>
        private int m_iTimeOut = 60;
        /// <summary>
        /// 最大发送次数（N）。
        /// </summary>
        /// <remarks>
        /// 网关与 SP 之间、网关之间的消息发送后等待 T 秒后未收到响应，应立即重发，再连续发送 N-1 次后仍未得到响应则停发。
        /// </remarks>
        private int m_iSendCount = 3;

        /// <summary>
        /// 是否是连接的
        /// </summary>
        private bool IsConnected { get; set; }

        /// <summary>
        /// 发送线程
        /// </summary>
        private Thread m_SendThread;

        /// <summary>
        /// 接受线程
        /// </summary>
        private Thread m_ReceiveThread;
        #endregion

        #region 公开属性


        /// <summary>
        /// 网关名称
        /// </summary>
        public string SPName { get; set; }


        /// <summary>
        /// 网关是否启动
        /// </summary>
        public bool IsStart { get; private set; }

        /// <summary>
        /// 等待发送的短信数量
        /// </summary>
        public int WaitSendCount
        {
            get
            {
                return m_iWaitSendCount;
            }
        }

        /// <summary>
        /// 网关命令事件
        /// </summary>
        public event EventHandler<CommandEventArg> CommandEvent;

        #endregion

        #region 私有变量


        /// <summary>
        /// 消息流水号（Messae Header）,顺序累加,步长为1,循环使用（每对请求和应答消息的流水号必须相同）。
        /// </summary>
        private static UInt32 m_iSeqID = 0;


        /// <summary>
        /// 长短信消息流水号
        /// </summary>
        private static UInt32 m_iLongSubmitSeqID = 0;

        private bool m_iIsFindWaitSubmits = false;

        private int m_iWaitSendCount = 0;

        /// <summary>
        /// 同步发送锁
        /// </summary>
        ManualResetEvent asyncWaitHandle = new ManualResetEvent(false);

        /// <summary>
        /// 提交长短信缓存
        /// </summary>
        Dictionary<UInt32, Long_SubmitMessage> SubmitLongMessageCache = new Dictionary<uint, Long_SubmitMessage>();

        /// <summary>
        /// 接收长短信缓存
        /// </summary>
        Dictionary<UInt32, SortedList<uint, CMPP_DELIVER>> DeliverLongMessageCache = new Dictionary<uint, SortedList<uint, CMPP_DELIVER>>();
        #endregion

        #region 公共方法

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="SPID"></param>
        /// <param name="PWD"></param>
        /// <param name="Address"></param>
        /// <param name="Port"></param>
        /// <param name="Protocol">协议版本</param>
        public SMSClient(string SPID, string PWD, string Address, int Port, ProtocolStrategy Protocol)
        {
            InitAllSlideWindow();
            SPName = "";
            m_strSPID = SPID;
            m_strPassword = PWD;
            m_strAddress = Address;
            m_iPort = Port;
            m_CMPPProtocol = Protocol;
        }

        /// <summary>
        /// 启动网关
        /// </summary>
        public bool Start()
        {
            if (!IsStart)
            {
                if (m_SendThread == null && m_ReceiveThread == null)
                {



                    InitAllSlideWindow();

                    if (Connect())
                    {

                        IsStart = true;


                        m_SendThread = new Thread(new ThreadStart(ThreadSend));
                        m_SendThread.IsBackground = true;
                        m_SendThread.Start();


                        m_ReceiveThread = new Thread(new ThreadStart(ThreadReceive));
                        m_ReceiveThread.IsBackground = true;
                        m_ReceiveThread.Start();



                        return true;
                    }
                    else
                    {
                        return false;
                    }
                }
                else
                {
                    return false;
                }
            }
            else
            {
                return true;
            }
        }

        /// <summary>
        /// 停止网关
        /// </summary>
        public void Stop()
        {
            if (IsStart)
            {
                if (m_SendThread != null && m_ReceiveThread != null)
                {
                    IsStart = false;
                    m_SendThread.Join();
                    m_SendThread = null;
                    m_ReceiveThread.Join();
                    m_ReceiveThread = null;
                    IsConnected = false;
                    m_TcpClient.Close();
                }
            }
        }


        /// <summary>
        /// 等待提交的消息
        /// </summary>
        /// <returns></returns>
        public IEnumerable<CMPP_SUBMIT> FindWaitSubmits()
        {

            lock (m_MESSAGEQueue)
            {
                lock (m_SlideWindow)
                {
                    m_iIsFindWaitSubmits = true;

                    List<CMPP_SUBMIT> submits = new List<CMPP_SUBMIT>();


                    if (m_MESSAGEQueue != null && m_MESSAGEQueue.Count != 0)
                    {
                        DATA_PACKAGE[] data_packages = m_MESSAGEQueue.ToArray();

                        foreach (DATA_PACKAGE data in data_packages)
                        {
                            if (data.Data is CMPP_SUBMIT)
                            {
                                submits.Add(data.Data as CMPP_SUBMIT);
                            }
                        }
                    }

                    foreach (MessageSlideWindow slide in m_SlideWindow)
                    {
                        if (slide.Status == SlideStatusType.Begin && slide.Message.Data is CMPP_SUBMIT)
                        {
                            submits.Add(slide.Message.Data as CMPP_SUBMIT);
                        }
                    }


                    foreach (CMPP_SUBMIT submit in submits)
                    {
                        yield return submit;
                    }


                    m_iIsFindWaitSubmits = false;
                }
            }
        }

        /// <summary>
        /// 取消发送短信(本地队列)
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        public bool CancelSendSubmit(CMPP_SUBMIT source)
        {
            if (m_iIsFindWaitSubmits)
            {
                return CancelSendSubmitImpl(source);
            }
            else
            {
                lock (m_MESSAGEQueue)
                {
                    lock (m_SlideWindow)
                    {
                        return CancelSendSubmitImpl(source);
                    }
                }
            }
        }

        /// <summary>
        /// 同步取消发送短信(服务器队列)
        /// </summary>
        /// <param name="msgid"></param>
        public CancelMessage CancelSubmitServer(ulong msgid)
        { 
            CancelMessage cancelmessage = null;
            
            BeginCancelSubmitServer(msgid,(result) =>
            {
                cancelmessage = result as CancelMessage;
                asyncWaitHandle.Set();
            }, null, true);

            asyncWaitHandle.WaitOne();
            asyncWaitHandle.Reset();

            return cancelmessage;
        }

        /// <summary>
        /// 异步取消发送短信(服务器队列)
        /// </summary>
        /// <param name="msgid">消息ID</param>
        /// <param name="action">回调函数</param>
        /// <param name="asyncstate">状态</param>
        /// <param name="_IsSynchronously">是否同步执行回调函数</param>
        public void BeginCancelSubmitServer(ulong msgid,Action<object> action,object asyncstate,bool _IsSynchronously)
        {
            CMPP_CANCEL cancel = new CMPP_CANCEL();
            cancel.Head = new CMPP_HEAD();
            cancel.Head.CommandID = SMSCommand.CMD_CANCEL;
            cancel.Head.SequenceID = CreateSeqID();

            cancel.MsgID = msgid;

            SendMessageQueue(cancel, action, asyncstate, _IsSynchronously);
        }
    
        #region 发送

        /// <summary>
        /// 同步发送短信
        /// </summary>
        /// <param name="_MsgContent">信息内容</param>
        /// <param name="_encoding">信息编码</param>
        /// <param name="_sourceID">SP的服务代码，将显示在最终用户手机上的短信主叫号码。</param>
        /// <param name="_destinations">接收短信的电话号码列表。</param>
        /// <param name="_serviceID">业务标识（如：woodpack）。</param>
        /// <param name="_needReport">是否要求返回状态报告。</param>
        /// <param name="_feeType">资费类别。</param>
        /// <param name="_feeUserType">计费用户。</param>
        /// <param name="_feeUser">被计费的号码（feeUserType 值为 FeeUser 时有效）。</param>
        /// <param name="_FeeCode">信息费（以“分”为单位，如：10 分代表 1角）。</param>
        /// <param name="_linkID">点播业务的 LinkID。</param>
        public void Send(
        string _MsgContent,
        CMPPEncoding _encoding,
        string _sourceID,
        string[] _destinations,
        string _serviceID,
        bool _needReport,
        CMPPFeeType _feeType,
        CMPPFeeUserType _feeUserType,
        string _feeUser,
        string _FeeCode,
        string _linkID)
        {
            CMPP_SUBMIT submit = CreateCMPPSubmit(_MsgContent, _encoding, _sourceID, _destinations, _serviceID, _needReport, _feeType, _feeUserType, _feeUser, _FeeCode, _linkID);

            Send(submit);
        }

        /// <summary>
        /// 同步提交短信
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        public SubmitMessage Send(CMPP_SUBMIT source)
        {
            SubmitMessage submitmessage = null;

            BeginSend(source, (result) =>
                {
                    submitmessage = result as SubmitMessage;
                    asyncWaitHandle.Set();
                }, null, true);

            asyncWaitHandle.WaitOne();
            asyncWaitHandle.Reset();

            return submitmessage;
        }

        /// <summary>
        /// 异步发送短信
        /// </summary>
        /// <param name="_MsgContent">信息内容</param>
        /// <param name="_encoding">信息编码</param>
        /// <param name="_sourceID">SP的服务代码，将显示在最终用户手机上的短信主叫号码。</param>
        /// <param name="_destinations">接收短信的电话号码列表。</param>
        /// <param name="_serviceID">业务标识（如：woodpack）。</param>
        /// <param name="_needReport">是否要求返回状态报告。</param>
        /// <param name="_feeType">资费类别。</param>
        /// <param name="_feeUserType">计费用户。</param>
        /// <param name="_feeUser">被计费的号码（feeUserType 值为 FeeUser 时有效）。</param>
        /// <param name="_FeeCode">信息费（以“分”为单位，如：10 分代表 1角）。</param>
        /// <param name="_linkID">点播业务的 LinkID。</param>
        /// <param name="action">回调方法</param>
        /// <param name="asyncState">状态</param>
        public void BeginSend(
                string _MsgContent,
                CMPPEncoding _encoding,
                string _sourceID,
                string[] _destinations,
                string _serviceID,
                bool _needReport,
                CMPPFeeType _feeType,
                CMPPFeeUserType _feeUserType,
                string _feeUser,
                string _FeeCode,
                string _linkID,
                Action<object> action,
                object asyncState)
        {
            CMPP_SUBMIT submit = CreateCMPPSubmit(_MsgContent, _encoding, _sourceID, _destinations, _serviceID, _needReport, _feeType, _feeUserType, _feeUser, _FeeCode, _linkID);

            BeginSend(submit, action, asyncState, false);
        }

        /// <summary>
        /// 异步提交短信
        /// </summary>
        /// <param name="source">短信</param>
        /// <param name="action">回调方法</param>
        /// <param name="asyncState">状态</param>
        /// <param name="_IsSynchronously">是否同步执行回调方法</param>
        public void BeginSend(CMPP_SUBMIT source, Action<object> action, object asyncState, bool _IsSynchronously)
        {
            List<CMPP_SUBMIT> submitSegments = new List<CMPP_SUBMIT>();

            #region 判断是否属于长短信

            if (CheckIsLongSubmit(source))
            {
                submitSegments = ConvertLongSubmit(source);
                SubmitLongMessageCache.Add(submitSegments[0].PkSequence,
                   new Long_SubmitMessage()
                   {
                       LongMessage = source,
                       MessageSegments = submitSegments.Cast<ICMPP_MESSAGE>().ToList(),
                       DeliverResps = new List<CMPP_SUBMIT_RESP>()
                   }
                   );
            }
            else
            {
                submitSegments.Add(source);
            }


            #endregion

            foreach (CMPP_SUBMIT submit in submitSegments)
            {
                submit.Head = new CMPP_HEAD();
                submit.Head.CommandID = SMSCommand.CMD_SUBMIT;
                submit.Head.SequenceID = CreateSeqID();


                SendMessageQueue(submit, action, asyncState, _IsSynchronously);
            }
        }


       
        #endregion


        /// <summary>
        /// 创建CMPP_SUBMIT结构
        /// </summary>
        /// <param name="_MsgContent">信息内容</param>
        /// <param name="_encoding">信息编码</param>
        /// <param name="_sourceID">SP的服务代码，将显示在最终用户手机上的短信主叫号码。</param>
        /// <param name="_destinations">接收短信的电话号码列表。</param>
        /// <param name="_serviceID">业务标识（如：woodpack）。</param>
        /// <param name="_needReport">是否要求返回状态报告。</param>
        /// <param name="_feeType">资费类别。</param>
        /// <param name="_feeUserType">计费用户。</param>
        /// <param name="_feeUser">被计费的号码（feeUserType 值为 FeeUser 时有效）。</param>
        /// <param name="_FeeCode">信息费（以“分”为单位，如：10 分代表 1角）。</param>
        /// <param name="_linkID">点播业务的 LinkID。</param>
        /// <returns>CMPP_SUBMIT结构</returns>
        public CMPP_SUBMIT CreateCMPPSubmit(
            string _MsgContent,
            CMPPEncoding _encoding,
            string _sourceID,
            string[] _destinations,
            string _serviceID,
            bool _needReport,
            CMPPFeeType _feeType,
            CMPPFeeUserType _feeUserType,
            string _feeUser,
            string _FeeCode,
            string _linkID)
        {
            CMPP_SUBMIT submit = new CMPP_SUBMIT();

            // 信息内容。
            submit.MsgContent = _MsgContent;
            // 信息编码。
            submit.MsgFmt = (byte)_encoding;
            // SP的服务代码，将显示在最终用户手机上的短信主叫号码。
            submit.SrcID = _sourceID;
            // 接收短信的电话号码列表。
            submit.DestTerminalID = _destinations;
            // 业务标识（如：woodpack）。
            submit.ServiceID = _serviceID;
            // 是否要求返回状态报告。
            submit.RegisteredDelivery = (byte)(_needReport ? 1 : 0);
            // 资费类别。
            submit.FeeType = string.Format("{0:D2}", (int)_feeType);
            // 计费用户。
            submit.FeeUserType = (byte)_feeUserType;
            // 被计费的号码（feeUserType 值为 FeeUser 时有效）。
            submit.FeeTerminalID = _feeUser;
            // 被计费号码的真实身份（“真实号码”或“伪码”）。
            submit.FeeTerminalType = 0;
            // 信息费（以“分”为单位，如：10 分代表 1角）。
            submit.FeeCode = _FeeCode.ToString();
            // 点播业务的 linkId。
            submit.LinkID = _linkID;

            return submit;
        }

        #endregion

        #region 保护方法

        /// <summary>
        /// 发送线程处理方法
        /// </summary>
        private void ThreadSend()
        {
            while (true)
            {
                try
                {

                    #region 判断是否停止

                    if (!IsStart)
                    {
                        break;
                    }

                    #endregion

                    #region 检查网络连接状态
                    if (!IsConnected)
                    {
                        RaiseCommandEvent(new CommandEventArg() { EventType = EventType.System, Data = "正在重新连接......" });
                        if (!Connect())
                        {
                            continue;
                        }
                    }
                    #endregion

                    #region 填充滑动窗口

                    lock (m_SlideWindow)
                    {
                        for (int i = 0; i < m_SlideWindow.Length; i++)
                        {
                            if (m_SlideWindow[i].Status == SlideStatusType.Wait)
                            {
                                DATA_PACKAGE data_package = MessageQueueDequeue();
                                if (data_package != null)
                                {
                                    m_SlideWindow[i].Message = data_package;
                                    m_SlideWindow[i].Status = SlideStatusType.Begin;
                                }
                            }
                        }
                    }
                    #endregion

                    #region 发送消息

                    lock (m_SlideWindow)
                    {
                        for (int i = 0; i < m_SlideWindow.Length; i++)
                        {

                            bool doSend = m_SlideWindow[i].Status == SlideStatusType.Begin;

                            if (m_SlideWindow[i].Status == SlideStatusType.Sending && (DateTime.Now - m_SlideWindow[i].SendTime.Value).TotalSeconds > m_iTimeOut)
                            {
                                //发送超过指定次数,引发错误事件
                                if (m_SlideWindow[i].SendCount > m_iSendCount)
                                {
                                    CommandEventArg args = null;

                                    #region CMPP_CANCEL
                                    if (m_SlideWindow[i].Message.Data.Head.CommandID == SMSCommand.CMD_CANCEL)
                                    {
                                        CMPP_CANCEL cancel = m_SlideWindow[i].Message.Data as CMPP_CANCEL;
                                        args = new CommandEventArg()
                                        {
                                            EventType = EventType.CancelError,
                                            Data = new CancelMessage()
                                            {
                                                Exception = new Exception("发送超时"),
                                                MsgId = cancel.MsgID
                                            }
                                        };
                                    } 
                                    #endregion

                                    #region CMD_SUBMIT
                                    if (m_SlideWindow[i].Message.Data.Head.CommandID == SMSCommand.CMD_SUBMIT)
                                    {
                                        CMPP_SUBMIT submit = m_SlideWindow[i].Message.Data as CMPP_SUBMIT;
                                        args = new CommandEventArg()
                                        {
                                            EventType = EventType.SubmitError,
                                            Data = new SubmitMessage()
                                            {
                                                Exception = new Exception("发送超时"),
                                                Submit = submit
                                            }
                                        };

                                        #region 是否属于长短信
                                        if (SubmitLongMessageCache.ContainsKey(submit.PkSequence))
                                        {
                                            (args.Data as SubmitMessage).Submit = SubmitLongMessageCache[submit.PkSequence].LongMessage as CMPP_SUBMIT;


                                            if (m_SlideWindow[i].Message.AsyncAction != null)
                                            {
                                                if (m_SlideWindow[i].Message.IsSynchronously)
                                                {
                                                    m_SlideWindow[i].Message.AsyncAction(m_SlideWindow[i].Message.AsyncState);
                                                }
                                            }

                                            foreach (CMPP_SUBMIT longsubmit in SubmitLongMessageCache[submit.PkSequence].MessageSegments)
                                            {

                                                for (int j = 0; i < m_SlideWindow.Length; j++)
                                                {
                                                    if (m_SlideWindow[j].Message != null)
                                                        if (longsubmit.Head.SequenceID == m_SlideWindow[j].Message.Data.Head.SequenceID)
                                                        {
                                                            m_SlideWindow[j] = new MessageSlideWindow();
                                                            InitSlideWindow(m_SlideWindow[j]);
                                                        }
                                                }
                                            }
                                            SubmitLongMessageCache.Remove(submit.PkSequence);
                                        }
                                        #endregion
                                    } 
                                    #endregion

                                    #region 初始化滑动窗口

                                    m_SlideWindow[i] = new MessageSlideWindow();
                                    InitSlideWindow(m_SlideWindow[i]);
                                    doSend = false; 
                                    #endregion

                                    #region 触发事件
                                    if (args != null)
                                    {
                                        RaiseCommandEvent(args);
                                    } 
                                    #endregion
                                }
                                else
                                {
                                    doSend = true;
                                }

                            }
                            if (doSend)
                            {
                                try
                                {
                                    byte[] data = null;
                                    try
                                    {
                                        data = m_CMPPProtocol.GetBytes(m_SlideWindow[i].Message.Data);
                                    }
                                    catch (Exception ex)
                                    {
                                        CommandEventArg args = new CommandEventArg()
                                        {
                                            EventType = EventType.System,
                                            Data = new SystemMessage(ex)
                                        };
                                        m_SlideWindow[i] = new MessageSlideWindow();
                                        InitSlideWindow(m_SlideWindow[i]);
                                        RaiseCommandEvent(args);
                                        continue;
                                    }

                                    m_NetworkStream.Write(data, 0, data.Length);

                                    if (m_SlideWindow[i].Message.Data is CMPP_SUBMIT)
                                        Interlocked.Decrement(ref m_iWaitSendCount);
                                    m_SlideWindow[i].Status = SlideStatusType.Sending;
                                    m_SlideWindow[i].SendTime = DateTime.Now;
                                    m_SlideWindow[i].SendCount++;
                                    m_iLastSendTime = DateTime.Now;
                                }
                                catch (Exception ex)
                                {
                                    IsConnected = false;
                                    RaiseCommandEvent(new CommandEventArg() { EventType = EventType.System, Data = new SystemMessage(ex) });
                                    continue;
                                }
                            }
                        }
                    }

                    #endregion

                    #region 激活测试

                    if ((DateTime.Now - m_iLastSendTime.Value).TotalSeconds > m_iActiveTestSpan)
                    {

                        CMPP_ACTIVE_TEST msg = new CMPP_ACTIVE_TEST();
                        msg.Head = new CMPP_HEAD();
                        msg.Head.CommandID = SMSCommand.CMD_ACTIVE_TEST;
                        msg.Head.SequenceID = CreateSeqID();


                        MessageQueueEnqueue(new DATA_PACKAGE() { Data = msg });
                    }

                    #endregion

                }
                catch (Exception ex)
                {
                    RaiseCommandEvent(new CommandEventArg() { EventType = EventType.System, Data = new SystemMessage(ex) });
                }
                Thread.Sleep(m_iSendSpan);
            }
        }


        /// <summary>
        /// 接收线程处理方法
        /// </summary>
        private void ThreadReceive()
        {
            while (true)
            {
                #region 判断是否停止

                if (!IsStart)
                {
                    break;
                }

                #endregion

                try
                {
                    if (m_TcpClient != null && IsConnected)
                    {
                        if (m_NetworkStream.DataAvailable)
                        {
                            CMPP_HEAD Head = ReadHead();
                            MessageSlideWindow _slidewindow = null;
                            CommandEventArg args = null;

                            #region 初始化SlideWindow
                            lock (m_SlideWindow)
                            {
                                for (int i = 0; i < m_SlideWindow.Length; i++)
                                {
                                    if (m_SlideWindow[i].Status == SlideStatusType.Sending && m_SlideWindow[i].Message.Data.Head.SequenceID == Head.SequenceID)
                                    {
                                        _slidewindow = m_SlideWindow[i];
                                        m_SlideWindow[i] = new MessageSlideWindow();
                                        InitSlideWindow(m_SlideWindow[i]);
                                    }
                                }
                            }
                            #endregion

                            switch (Head.CommandID)
                            {
                                case SMSCommand.CMD_SUBMIT_RESP:
                                    #region CMD_SUBMIT_RESP

                                    if (_slidewindow != null)
                                    {

                                        CMPP_SUBMIT submit = _slidewindow.Message.Data as CMPP_SUBMIT;
                                        CMPP_SUBMIT_RESP submitresp = ReadCMPPMessage<CMPP_SUBMIT_RESP>(Head);

                                        args = new CommandEventArg()
                                        {
                                            EventType = EventType.SubmitSuccess,
                                            Data = new SubmitMessage()
                                            {
                                                Submit_Resp = submitresp,
                                                Submit = _slidewindow.Message.Data as CMPP_SUBMIT,
                                                AsyncState = _slidewindow.Message.AsyncState
                                            }
                                        };


                                        #region 是否属于长短信
                                        if (SubmitLongMessageCache.ContainsKey(submit.PkSequence))
                                        {
                                            SubmitLongMessageCache[submit.PkSequence].DeliverResps.Add(submitresp);

                                            if (SubmitLongMessageCache[submit.PkSequence].DeliverResps.Count == SubmitLongMessageCache[submit.PkSequence].MessageSegments.Count)
                                            {

                                                (args.Data as SubmitMessage).Submit = SubmitLongMessageCache[submit.PkSequence].LongMessage as CMPP_SUBMIT;
                                                if (!SubmitLongMessageCache[submit.PkSequence].DeliverResps.TrueForAll(p => p.Result == 0))
                                                {
                                                    submitresp = SubmitLongMessageCache[submit.PkSequence].DeliverResps.FirstOrDefault(p => p.Result != 0);
                                                }

                                                SubmitLongMessageCache.Remove(submit.PkSequence);
                                            }
                                            else
                                            {
                                                continue;
                                            }
                                        }
                                        #endregion

                                        if (submitresp.Result == 0)
                                        {
                                            args.EventType = EventType.SubmitSuccess;
                                        }
                                        else
                                        {
                                            args.EventType = EventType.SubmitError;
                                            (args.Data as SubmitMessage).Exception = new Exception(submitresp.ToString());
                                        }

                                    }
                                    #endregion
                                    break;
                                case SMSCommand.CMD_DELIVER:
                                    #region CMD_DELIVER

                                    CMPP_DELIVER deliver = ReadCMPPMessage<CMPP_DELIVER>(Head);

                                    if (deliver.RegisteredDelivery == 1)
                                    {
                                        #region 触发事件
                                        args = new CommandEventArg()
                                                                    {
                                                                        EventType = EventType.Report,
                                                                        Data = new DeliverMessage()
                                                                        {
                                                                            Deliver = deliver
                                                                        }
                                                                    };
                                        #endregion
                                    }
                                    else
                                    {
                                        if (deliver.PkTotal > 1)
                                        {
                                            #region 长短信接收
                                            if (!DeliverLongMessageCache.ContainsKey(deliver.PkSequence))
                                            {
                                                DeliverLongMessageCache.Add(deliver.PkSequence, new SortedList<uint, CMPP_DELIVER>());
                                            }
                                            DeliverLongMessageCache[deliver.PkSequence].Add(deliver.PkNumber, deliver);


                                            #region 合并长短信
                                            if (DeliverLongMessageCache[deliver.PkSequence].Count == deliver.PkTotal)
                                            {
                                                string msgcontent = "";
                                                foreach (CMPP_DELIVER subdeliver in DeliverLongMessageCache[deliver.PkSequence].Values)
                                                {
                                                    msgcontent += subdeliver.MsgContent;
                                                }

                                                deliver.MsgContent = msgcontent;

                                                args = new CommandEventArg()
                                                {
                                                    EventType = EventType.Deliver,
                                                    Data = new DeliverMessage()
                                                    {
                                                        Deliver = deliver
                                                    }
                                                };

                                                DeliverLongMessageCache.Remove(deliver.PkSequence);
                                            }
                                            #endregion

                                            #endregion
                                        }
                                        else
                                        {
                                            #region 触发事件
                                            args = new CommandEventArg()
                                                                            {
                                                                                EventType = EventType.Deliver,
                                                                                Data = new DeliverMessage()
                                                                                {
                                                                                    Deliver = deliver
                                                                                }
                                                                            };
                                            #endregion
                                        }
                                    }


                                    #region 提交CMPP_DELIVER_RESP

                                    CMPP_DELIVER_RESP deliverresp = new CMPP_DELIVER_RESP();
                                    deliverresp.Head = new CMPP_HEAD();
                                    deliverresp.Head.CommandID = SMSCommand.CMD_DELIVER_RESP;
                                    deliverresp.Head.SequenceID = deliver.Head.SequenceID;
                                    deliverresp.MsgID = deliver.MsgID;
                                    deliverresp.Result = 0;

                                    MessageQueueEnqueue(new DATA_PACKAGE() { Data = deliverresp });

                                    #endregion

                                    #endregion
                                    break;
                                case SMSCommand.CMD_ACTIVE_TEST_RESP:
                                    #region CMD_ACTIVE_TEST_RESP
                                    CMPP_ACTIVE_TEST_RESP testresp = ReadCMPPMessage<CMPP_ACTIVE_TEST_RESP>(Head);
                                    #endregion
                                    break;
                                case SMSCommand.CMD_CANCEL_RESP:
                                    #region CMD_CANCEL_RESP
                                    if (_slidewindow != null)
                                    {
                                        CMPP_CANCEL cancel = _slidewindow.Message.Data as CMPP_CANCEL;
                                        CMPP_CANCEL_RESP cancelresp = ReadCMPPMessage<CMPP_CANCEL_RESP>(Head);

                                        args = new CommandEventArg()
                                        {
                                            EventType = EventType.CancelError,
                                            Data = new CancelMessage()
                                            {
                                                Cancel_Resp = cancelresp,
                                                MsgId = cancel.MsgID
                                            }
                                        };

                                        if (cancelresp.SuccessID == 0)
                                        {
                                            args.EventType = EventType.CancelSuccess;
                                        }
                                        else
                                        {
                                            args.EventType = EventType.CancelError;
                                            (args.Data as CancelMessage).Exception = new Exception(cancelresp.ToString());
                                        }


                                        
                                    }
                                    #endregion
                                    break;
                            }

                            #region 执行异步回调函数

                            if (_slidewindow != null && args!=null)
                            {
                                if (_slidewindow.Message.AsyncAction != null)
                                {
                                    if (_slidewindow.Message.IsSynchronously)
                                    {
                                        _slidewindow.Message.AsyncAction(args.Data);
                                    }
                                    else
                                    {
                                        Task.Factory.StartNew(() =>
                                        {
                                            _slidewindow.Message.AsyncAction(args.Data);
                                        }, TaskCreationOptions.PreferFairness);
                                    }
                                }
                            }


                            #endregion

                            #region 触发事件
                            if (args != null)
                            {
                                RaiseCommandEvent(args);
                            } 
                            #endregion
                        }
                    }
                }
                catch (Exception ex)
                {
                    RaiseCommandEvent(new CommandEventArg() { EventType = EventType.System, Data = new SystemMessage(ex) });
                }


                Thread.Sleep(m_iReceiveSpan);
            }
        }

        /// <summary>
        /// 连接到服务器
        /// </summary>
        /// <returns></returns>
        private bool Connect()
        {
            try
            {
                m_TcpClient = new TcpClient();
                m_TcpClient.ReceiveTimeout = m_TcpClient.SendTimeout = m_iTcpClientTimeout * 1000;
                
                m_TcpClient.Connect(m_strAddress, m_iPort);
                m_NetworkStream = m_TcpClient.GetStream();

                CMPP_CONNECT conn = new CMPP_CONNECT();
                conn.Head = new CMPP_HEAD();
                conn.Head.CommandID = SMSCommand.CMD_CONNECT;
                conn.Head.SequenceID = CreateSeqID();

                DateTime dt = DateTime.Now;
                conn.SourceAddress = m_strSPID;
                conn.TimeStamp = System.Convert.ToUInt32(string.Format("{0:MMddhhmmss}", dt));
                conn.AuthenticatorSource = CreateDigest(dt);




                byte[] bytes = m_CMPPProtocol.GetBytes(conn);
                m_NetworkStream.Write(bytes, 0, bytes.Length);

                m_iLastSendTime = DateTime.Now;


                // 等待 RESPONSE 5 秒。
                int i;
                for (i = 0; i < 5000 && !m_NetworkStream.DataAvailable; i += 10)
                {
                    Thread.Sleep(10);
                }

                if (i >= 5000)
                {
                    RaiseCommandEvent(new CommandEventArg()
                    {
                        EventType = EventType.System,
                        Data = new SystemMessage(new Exception("连接超时"))
                    });
                    return false;
                }


                CMPP_HEAD head = ReadHead();
                CMPP_CONNECT_RESP resp = ReadCMPPMessage<CMPP_CONNECT_RESP>(head);

                if (resp == null)
                {
                    RaiseCommandEvent(new CommandEventArg()
                    {
                        EventType = EventType.System,
                        Data = new SystemMessage(new Exception("未接收到正确的 CONNECT_RESP"))
                    });
                    return false;
                }

                if (resp.Status != 0)
                {
                    RaiseCommandEvent(new CommandEventArg()
                    {
                        EventType = EventType.System,
                        Data = new SystemMessage(new Exception("连接错误:" + resp.ToString()))
                    });
                    return false;
                }

                RaiseCommandEvent(new CommandEventArg()
                {
                    EventType = EventType.System,
                    Data = new SystemMessage("连接成功")
                });

                IsConnected = true;
                return true;
            }
            catch (Exception ex)
            {
                RaiseCommandEvent(new CommandEventArg()
                {
                    EventType = EventType.System,
                    Data = new SystemMessage(ex)
                });
                return false;
            }
        }
        /// <summary>
        /// 检查是否是长短信
        /// </summary>
        /// <param name="submit"></param>
        /// <returns></returns>
        private bool CheckIsLongSubmit(CMPP_SUBMIT submit)
        {
            int MsgCount = CMPPProtocol.Convert.ToBytes(submit.MsgContent, (CMPPEncoding)submit.MsgFmt).Length;
            if (MsgCount > 140)
                return true;
            else
                return false;
        }
        /// <summary>
        /// 转换长短信
        /// </summary>
        /// <param name="submit"></param>
        /// <returns></returns>
        private List<CMPP_SUBMIT> ConvertLongSubmit(CMPP_SUBMIT submit)
        {
            string msgContent = submit.MsgContent;
            List<CMPP_SUBMIT> list = new List<CMPP_SUBMIT>();
            if (CheckIsLongSubmit(submit))
            {
                List<string> msglist = new List<string>();
                while (msgContent.Length > 0)
                {
                    if (msgContent.Length > 67)
                    {
                        string temp = msgContent.Substring(0, 67);
                        msglist.Add(temp);
                        msgContent = msgContent.Substring(67);
                    }
                    else
                    {
                        msglist.Add(msgContent);
                        msgContent = "";
                    }
                }
                uint Sequence = CreateLongSubmitSeqID();



                for (int i = 1; i <= msglist.Count; i++)
                {
                    CMPP_SUBMIT submitSegment = submit.Clone() as CMPP_SUBMIT;

                    submitSegment.PkNumber = (byte)i;
                    submitSegment.PkTotal = (byte)msglist.Count;
                    submitSegment.PkSequence = Sequence;
                    submitSegment.MsgContent = msglist[i - 1];

                    list.Add(submitSegment);
                }
            }
            else
            {
                throw new SMSCoreException("不是一个长短信");
            }
            return list;
        }
        /// <summary>
        /// 触发事件
        /// </summary>
        /// <param name="arg">事件参数</param>
        private void RaiseCommandEvent(CommandEventArg arg)
        {
            if (this.CommandEvent != null)
            {
                arg.Client = this;
                arg.DateTime = DateTime.Now;
                Task.Factory.StartNew(() =>
                {
                    this.CommandEvent(this, arg);
                }, TaskCreationOptions.PreferFairness);
            }
        }
        /// <summary>
        /// 初始化滑动窗口
        /// </summary>
        private void InitAllSlideWindow()
        {
            for (int i = 0; i < m_SlideWindow.Length; i++)
            {
                m_SlideWindow[i] = new MessageSlideWindow();
                InitSlideWindow(m_SlideWindow[i]);
            }
        }
        /// <summary>
        /// 初始化单个滑动窗口
        /// </summary>
        /// <param name="slidewindow"></param>
        private void InitSlideWindow(MessageSlideWindow slidewindow)
        {
            slidewindow.Message = null;
            slidewindow.SendCount = 0;
            slidewindow.SendTime = null;
            slidewindow.Status = SlideStatusType.Wait;
        }
        /// <summary>
        /// 添加消息到消息队列
        /// </summary>
        /// <param name="data_package">消息</param>
        private void MessageQueueEnqueue(DATA_PACKAGE data_package)
        {
            lock (m_MESSAGEQueue)
            {
                m_MESSAGEQueue.Enqueue(data_package);
                Interlocked.Increment(ref m_iWaitSendCount);
            }
        }

        /// <summary>
        /// 发送消息到队列
        /// </summary>
        /// <param name="message">消息</param>
        /// <param name="action">回调函数</param>
        /// <param name="asyncState">状态</param>
        /// <param name="_IsSynchronously">是否同步执行回调函数</param>
        private void SendMessageQueue(ICMPP_MESSAGE message, Action<object> action, object asyncState, bool _IsSynchronously)
        {

            if (!IsStart)
            {
                throw new SMSCoreException("服务没有启动");
            }

            DATA_PACKAGE data_package = new DATA_PACKAGE();

            data_package.IsSynchronously = _IsSynchronously;

            data_package.Data = message;
            data_package.AsyncAction = action;
            data_package.AsyncState = asyncState;

            MessageQueueEnqueue(data_package);
        }

        /// <summary>
        /// 取消提交短信
        /// </summary>
        /// <param name="submit"></param>
        /// <returns></returns>
        private bool CancelSendSubmitImpl(CMPP_SUBMIT submit)
        {
            if (m_MESSAGEQueue != null && m_MESSAGEQueue.Count != 0)
            {
                List<DATA_PACKAGE> data_packages = m_MESSAGEQueue.ToArray().ToList();
                DATA_PACKAGE data_package = data_packages.SingleOrDefault(p => p.Data == submit);

                if (data_package != null)
                {
                    data_packages.Remove(data_package);
                    m_MESSAGEQueue.Clear();
                    foreach (DATA_PACKAGE data in data_packages)
                    {
                        m_MESSAGEQueue.Enqueue(data);
                    }
                    return true;
                }
            }

            foreach (MessageSlideWindow slide in m_SlideWindow)
            {
                if (slide.Status == SlideStatusType.Begin && slide.Message.Data == submit)
                {
                    InitSlideWindow(slide);
                    return true;
                }
            }

            return false;
        }
        /// <summary>
        /// 从消息队列取出消息
        /// </summary>
        /// <returns></returns>
        private DATA_PACKAGE MessageQueueDequeue()
        {
            DATA_PACKAGE data_package;
            lock (m_MESSAGEQueue)
            {
                if (m_MESSAGEQueue.Count > 0)
                    data_package = m_MESSAGEQueue.Dequeue();
                else
                    data_package = null;
            }
            return data_package;
        }
        /// <summary>
        /// 读取“消息头”。
        /// </summary>
        private CMPP_HEAD ReadHead()
        {
            CMPP_HEAD head = new CMPP_HEAD();
            head.CommandID = 0;
            try
            {
                if (m_NetworkStream.DataAvailable)
                {
                    byte[] buffer = new byte[4];
                    m_NetworkStream.Read(buffer, 0, buffer.Length);
                    Array.Reverse(buffer);
                    head.TotalLength = BitConverter.ToUInt32(buffer, 0);

                    m_NetworkStream.Read(buffer, 0, buffer.Length);
                    Array.Reverse(buffer);
                    head.CommandID = BitConverter.ToUInt32(buffer, 0);

                    m_NetworkStream.Read(buffer, 0, buffer.Length);
                    Array.Reverse(buffer);
                    head.SequenceID = BitConverter.ToUInt32(buffer, 0);
                }
            }
            catch
            {
                head.CommandID = SMSCommand.CMD_ERROR;
            }
            return head;
        }

        /// <summary>
        /// 读取消息
        /// </summary>
        /// <typeparam name="T">消息类型</typeparam>
        /// <param name="msghead">消息头</param>
        /// <returns>消息</returns>
        private T ReadCMPPMessage<T>(CMPP_HEAD msghead) where T : ICMPP_MESSAGE, new()
        {
            T result = default(T);
            if (m_NetworkStream.DataAvailable)
            {
                byte[] buffer = new byte[msghead.TotalLength - 12];
                m_NetworkStream.Read(buffer, 0, buffer.Length);
                result = m_CMPPProtocol.FromBytes<T>(buffer);
                result.Head = msghead;
            }
            return result;
        }

        /// <summary>
        /// 计算 CMPP_CONNECT 包的 AuthenticatorSource 字段。
        /// </summary>
        /// <remarks>
        /// MD5(Source_Addr + 9字节的0 + shared secret + timestamp);
        /// </remarks>
        private byte[] CreateDigest(DateTime dt)
        {
            byte[] btContent = new byte[25 + m_strPassword.Length];
            Array.Clear(btContent, 0, btContent.Length);

            // Source_Addr，SP的企业代码（6位）。
            int iPos = 0;
            foreach (char ch in m_strSPID)
            {
                btContent[iPos] = (byte)ch;
                iPos++;
            }

            // 9字节的0。
            iPos += 9;

            // password，由 China Mobile 提供（长度不固定）。
            foreach (char ch in m_strPassword)
            {
                btContent[iPos] = (byte)ch;
                iPos++;
            }

            // 时间戳（10位）。
            foreach (char ch in string.Format("{0:MMddhhmmss}", dt))
            {
                btContent[iPos] = (byte)ch;
                iPos++;
            }
            return new MD5CryptoServiceProvider().ComputeHash(btContent);
        }

        /// <summary>
        /// 创建消息流水号。
        /// </summary>
        /// <remarks>
        /// 消息流水号，顺序累加，步长为 1，循环使用（每对请求和应答消息的流水号必须相同）。
        /// </remarks>
        protected uint CreateSeqID()
        {
            return m_iSeqID++;
        }

        /// <summary>
        /// 创建长消息流水号。
        /// </summary>
        protected uint CreateLongSubmitSeqID()
        {
            return m_iLongSubmitSeqID++;
        }

        #endregion
    }
}
